/*********************************************************************************
 *      Copyright:  (C) 2022 Zhang xiangyun<1327295677@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(20/04/22)
 *         Author:  Zhang xiangyun <1327295677@qq.com>
 *      ChangeLog:  1, Release initial version on "20/04/22 21:11:39"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#include <sqlite3.h>

#include "sqlite.h"
#include "logger.h"
#include "server.h"

#define MAX_EVENTS            512
#define ARRAR_SIZE(x)         (sizeof(x)/sizeof(x[0]))
#define TABLENAME             "serverdata"

static inline void  print_usage(char *progname);


int main(int argc, char **argv)
{
	int                  listen_fd=-1;
	int                  client_fd=-1;
	int                  serv_port=0;
	int                  daemon_run=0;
	char                *progname=NULL;
	int                  opt;
	int                  rv=-1;
	int                  i;
	int                  j=0;
	int                  found;
	char                 buf[1024];
	char                *ptr=NULL;


	int                  epollfd=-1;
	struct epoll_event   event;
	struct epoll_event   event_array[MAX_EVENTS];
	int                  events;

	sqlite3             *db;
	int                  table_exist=0;
	int                  sqlite_rv=-1;
	char                *data[3];


	/* set input options */
	struct option        long_options[]=
	{
		{"daemon",no_argument,NULL,'b'},
		{"port",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{"NULL",0,NULL,0}
	};

	progname = basename(argv[0]);

	while((opt = getopt_long(argc,argv,"bp:h",long_options,NULL))!=-1)
	{
		switch(opt)
		{
			case 'b':
				daemon_run=1;
				break;
			case 'p':
				serv_port = atoi(optarg);
				break;
			case 'h':
				print_usage(progname);
				return EXIT_SUCCESS;
			default:
				break;
		}
	}

	if(!serv_port)
	{
		print_usage(progname);
		return -1;
	}

	/* set max open socket count */
	set_socket_rlimit();

	if((listen_fd=socket_server_init(NULL,serv_port))<0)
	{
		printf("ERROR:%s server listen on port %d failure\n",argv[0],serv_port);
		return -2;
	}
	printf("%s server start to listen on port %d\n",argv[0],serv_port);

	/* set program running on background */
	if(daemon_run)
	{
		daemon(0,0);
	}

	/* initial logger system */
	if(logger_init("stdout",LOG_LEVEL_DEBUG)<0)
	{
		fprintf(stderr,"initial logger system failure\n");
		return 1;
	}

	log_info("\n");
	log_info("------------SERVER START-------------\n");
	log_info("\n");




	if((epollfd=epoll_create(MAX_EVENTS))<0)
	{
		log_error("epoll create failure:%s\n",strerror(errno));
		return -3;
	}

	/* epoll event init */
	event.events = EPOLLIN;
	event.data.fd = listen_fd;

	if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_fd,&event)<0)
	{
		log_error("epoll add listen socket failure:%s\n",strerror(errno));
		return -4;
	}

	/* sqlite3 databese init */
	sqlite_rv=sqlite_init(&db);
	if(sqlite_rv<0)
	{
		goto CleanUp;
	}

	/*create database named server*/
	table_exist=determine_table_exist(TABLENAME,db);
	if(table_exist==1)
	{
		sqlite_rv=sqlite_create_table(TABLENAME,db);
		if(sqlite_rv<0)
		{
			goto CleanUp;
		}
	}





	for(;;)
	{
		/* blocked program */
		events = epoll_wait(epollfd,event_array,MAX_EVENTS,-1);
		if(events<0)
		{
			log_error("epoll failure:%s\n",strerror(errno));
			break;
		}
		else if(events==0)
		{
			log_error("epoll get timeout\n");
			continue;
		}

		/* rv>0 means the active events count */
		for(i=0;i<events;i++)
		{
			if((event_array[i].events==EPOLLERR) || (event_array[i].events==EPOLLHUP))
			{
				log_info("epoll_wait get error on fd[%d]: %s\n",event_array[i].data.fd,strerror(errno));
				epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
				close(event_array[i].data.fd);
			}

			/* listen socket get event means new client start connect now */
			if(event_array[i].data.fd == listen_fd)
			{
				if((client_fd=accept(listen_fd,(struct sockaddr *)NULL,NULL))<0)
				{
					log_info("accept new client failure:%s\n",strerror(errno));
					continue;
				}

				event.data.fd=client_fd;
				event.events=EPOLLIN;
				if(epoll_ctl(epollfd,EPOLL_CTL_ADD,client_fd,&event)<0)
				{
					log_error("epoll add client socket failure:%s\n",strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				log_info("epoll add new client socket[%d] ok.\n",client_fd);
			}

			/* already connected client socket get data incoming */
		
			else		
			{
				memset(buf,0,sizeof(buf));				
				if((rv=read(event_array[i].data.fd,buf,sizeof(buf)))<=0)
				{
					log_error("socket[%d] read failure or get disconnect and will be removed.\n",event_array[i].data.fd);
					epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
					close(event_array[i].data.fd);
			    	continue;
				}
				/* 
				else if(rv==0)
				{
					printf("client[%d]disconnect!\n",event_array[i].data.fd);
					continue;
				}
				*/

				else
				{
					//log_debug("%s\n",buf);
					ptr=strtok(buf,"/");
					j=0;
					while(ptr)
					{
						data[j]=ptr;
						++j;
						ptr=strtok(NULL,"/");
					}

					//epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
					//close(event_array[i].data.fd);
					sqlite_data_insert(data,TABLENAME,db);
					//sqlite3_close(db);

				}
			}
		
		}
	}

CleanUp:
	close(listen_fd);
	sqlite3_close(db);
	return 0;

}


static inline void print_usage(char *progname)
{
	printf("usage: %s[OPTION]...\n",progname);
	printf("%s is a socket server program,which used to connect socket client\n");
	printf("\nMandatory arguments to long options are mandatory for short options too:\n");
	printf("-b[daemon]     set program running on background\n");
	printf("-p[port]       set socket server port address\n");
	printf("-h[help]       display this help information\n");
	printf("\nEXAMPLE: %s -b-p 7300\n",progname);
	return ;

}
/*  
void set_socket_rlimit(void)
{
	struct rlimit limit ={0};
	getrlimit(RLIMIT_NOFILE,&limit);
	limit.rlim_cur = limit.rlim_max;
	setrlimit(RLIMIT_NOFILE,&limit);
	printf("set socket oprn fd max count to %d\n",limit.rlim_max);
}

int socket_server_init(char *listen_ip, int listen_port)
{
	struct sockaddr_in             servaddr;
	int                            rv=0;
	int                            on=1;
	int                            listen_fd=-1;

	if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
	{
		log_error("use socket() to create a TCP socket failure: %s\n",strerror(errno));
		return -1;
	}

	
	setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(listen_port);

	if(!listen_ip)
	{
		servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	}
	else 
	{
		if(inet_pton(AF_INET,listen_ip,&servaddr.sin_addr)<=0)
		{
			log_error("inet_pton() set ip address of listen_ip failure:%s",strerror(errno));
			rv=-2;
			goto CleanUp;
		}
	}
	if(bind(listen_fd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
	{
		log_error("use bind() to bind the TCP socket failure:%s\n",strerror(errno));
		rv=-3;
		goto CleanUp;
	}
	
	if(listen(listen_fd,64)<0)
	{
		log_error("listen()failure:%s\n",strerror(errno));
		rv=-4;
		goto CleanUp;
	}

CleanUp:
	if(rv<0)
	{
		close(listen_fd);
	}
	else
		rv=listen_fd;
	return rv;
}

*/
